package com.iwhalecloud.citybrain.flow.platform.manager.application.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSONObject;
import com.iwhalecloud.citybrain.flow.platform.manager.application.ConvertUtil.FormProcessInstanceHandleRelConvertUtil;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.FormFieldLimitEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.ProcessInstanceStatusEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.TaskLinkStatusEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.event.CreateParticipantEvent;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.threadlocal.CommonOperateLogHolder;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.CallbackThirdPlatformAfterTaskCreatedService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.UserService;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.impl.task.SyncProcessTaskProcessor;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.SpringUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ConstanceVariable;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ErrorCode;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ResponseResult;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.HttpCommandDto;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.HttpCommandResultDto;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.HttpCompleteWorkItemDto;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.enums.CommentTypeEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.enums.ReadOperateTypeEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowProcessInstanceTraceVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.form.FormFieldLimitDomain;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.form.FormInstanceDto;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.form.FormProcessInstanceHandleRelDto;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.monitor.FlowTaskTraceVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.userentity.UserInfo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowFormInstanceRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowFormRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowProcessInstanceHandleRelRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowProcessInstanceTraceRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowTaskService;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import static com.iwhalecloud.citybrain.flow.platform.manager.common.ConstanceVariable.MUST_EXPRESS;


/**
 * @author lilh
 * @date 2019-02-20 15:04
 */
@Service
public class CompleteWorkItemCommandHandler extends AbstractCommandHandler {

    @Autowired
    private TaskService taskService;

    @Autowired
    private CallbackThirdPlatformAfterTaskCreatedService callbackThirdPlatformAfterTaskCreatedService;

    @Autowired
    private TaskExecutor taskExecutor;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private FlowFormInstanceRepository formInstanceRepository;

    @Autowired
    private FlowProcessInstanceTraceRepository flowProcessInstanceTraceRepository;

    @Autowired
    private FlowFormRepository flowFormRepository;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private FlowProcessInstanceHandleRelRepository handleRelRepository;

    @Autowired
    protected ManagementService managementService;

    @Autowired
    private UserService userService;

    @Autowired
    private FlowTaskService flowTaskService;

    @Autowired
    private SyncProcessTaskProcessor syncProcessTaskProcessor;

    /**
     * 阅办任务跳转地址
     */
    @Value("${task.readDoJumpUrl:http://flow-platform-web-daily.ingress.dayu.work/todo/templatepage?actype=todo&processInstanceId=:processInstanceId&taskId=:taskId}")
    private String readDoJumpUrl;

    /**
     * 任务来源应用编码
     */
    @Value("${task.sourceAppCode:mes2}")
    private String sourceAppCode;

    //后加签
    public static final String AFTER_ADDSIGN = "after";

    @Override
    public String handleType() {
        return COMPLETE_WORK_ITEM;
    }



    @Override
    public HttpCommandResultDto handle(String params) {
        HttpCompleteWorkItemDto completeWorkItemDto = JSONObject.parseObject(params, HttpCompleteWorkItemDto.class);
        return handle(completeWorkItemDto);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public HttpCommandResultDto handle(HttpCommandDto commandDto) {
        HttpCompleteWorkItemDto completeWorkItemDto = (HttpCompleteWorkItemDto) commandDto;
        recordRequestInfoToThreadLocal(completeWorkItemDto);

        if (StringUtils.isBlank(completeWorkItemDto.getParticipantId())){
            return resolveResult("完成任务的处理人[participantId]不能为空",null);
        }
        try {
            UserInfo userInfo = userService.getUserInfoObj(completeWorkItemDto.getParticipantId());
            if (Objects.isNull(userInfo)){
                return resolveResult("完成任务的处理人[participantId="+completeWorkItemDto.getParticipantId()+"]不正确，请核实后重新调用",null);
            }
        } catch (NumberFormatException e) {
            return resolveResult("完成任务的处理人[participantId="+completeWorkItemDto.getParticipantId()+"]不正确，请核实后重新调用",null);
        }

        FlowTaskTraceVo flowTaskTraceVo = flowTaskService.getFlowTaskByTaskIdOrUserCode(completeWorkItemDto.getWorkItemId(),completeWorkItemDto.getParticipantId());
        if (Objects.isNull(flowTaskTraceVo)){
            return resolveResult(ErrorCode.NOT_TASK_CANDIDATE_OR_TASK_NOT_EXIST.errMessage(),null);
        }else{
            if (!Objects.equals(flowTaskTraceVo.getStatus(),ProcessInstanceStatusEnum.NORMAL.getCode())){
                return resolveFailResult(completeWorkItemDto.getProcessInstanceId(), ErrorCode.TASK_STATUS_NOT_NORMAL.errMessage());
            }
        }

        //1.判断流程实例是否存在，如果不存在，则直接返回
        ProcessInstance processInstance = resolveProcessInstance(completeWorkItemDto.getProcessInstanceId());
        if (Objects.isNull(processInstance)) {
            return resolveFailResult(completeWorkItemDto.getProcessInstanceId(), ErrorCode.PROCESS_INSTANCE_NOT_EXIST.errMessage());
        }

        //2.查询对应的任务是否存在，若不存在，则返回，若存在，则办理
        Task task = resolveTask(completeWorkItemDto);
        if (Objects.isNull(task)) {
            return resolveFailResult(completeWorkItemDto.getProcessInstanceId(), ErrorCode.WORK_ITEM_NOT_EXIST.errMessage());
        }

        String comment = completeWorkItemDto.getComment();
        if (StringUtils.isBlank(comment)){
            boolean need = checkIsRequiredComment(completeWorkItemDto.getWorkItemId());
            if (need){
                return resolveFailResult(completeWorkItemDto.getProcessInstanceId(), ErrorCode.COMMENT_NOT_EXIST.errMessage());
            }
        }else{
            addComment(completeWorkItemDto.getWorkItemId(),completeWorkItemDto.getParticipantId(),task.getProcessInstanceId(), CommentTypeEnum.SP.getName(),comment);
        }

        //是否强制表态
         //如果强制表态，则必须包含key: approval
        if(checkIsRequired(completeWorkItemDto.getWorkItemId(), MUST_EXPRESS)){
            if(Objects.isNull(completeWorkItemDto.getFlowPassList()) || !completeWorkItemDto.getFlowPassList().containsKey("approval")){
                return resolveFailResult(completeWorkItemDto.getProcessInstanceId(), "当前任务是强制表态，flowPassList中必须包含参数：approval");
            }
        }

        String formKey = task.getFormKey();
        if (StringUtils.isNotBlank(formKey)){
            String formId = flowFormRepository.findFormIdByFormKey(formKey);
            if (StringUtils.isNotBlank(formId)){

                String processDefinitionId = processInstance.getProcessDefinitionId();
                ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
                String deploymentId = processDefinition.getDeploymentId();

                List<FormFieldLimitDomain> fieldLimitDomainList = flowFormRepository.getFormFieldLimitByNodeCode(deploymentId,formId,task.getTaskDefinitionKey());
                if (Objects.nonNull(fieldLimitDomainList) && !fieldLimitDomainList.isEmpty()){
                    for (FormFieldLimitDomain formFieldLimitDomain : fieldLimitDomainList) {
                        Integer fieldLimit = formFieldLimitDomain.getFieldLimit();
                        if (((fieldLimit >> FormFieldLimitEnum.REQUIRED.getBit()) & 1) == 1){
                            Map flowParamList = completeWorkItemDto.getFlowParamList();
                            Map flowPassList = completeWorkItemDto.getFlowPassList();
                            if ((MapUtils.isEmpty(flowParamList) || !flowParamList.containsKey(formFieldLimitDomain.getFieldId()))
                                    && (MapUtils.isEmpty(flowPassList) || !flowPassList.containsKey(formFieldLimitDomain.getFieldId()))) {
                                return resolveFailResult(completeWorkItemDto.getProcessInstanceId(), ErrorCode.FORM_FIELD_NOT_EXIST.errMessage());
                            }
                        }
                    }
                }
            }else{
                logger.error("formKey is non null,but formId is null");
            }
        }


        //将flowPassList设置为流程变量
        runtimeService.setVariables(processInstance.getId(), completeWorkItemDto.getFlowPassList());
        recordTaskInfoToThreadLocal(task);
        Map<String, Object> variables = runtimeService.getVariables(processInstance.getProcessInstanceId());
        //3.获取完成任务前，同一实例的运行中的任务
        List<Task> beforeCompleteTasks = getCurrentTasks(processInstance);

        //4.办理任务
        long completeStart = System.currentTimeMillis();
        completeTask(task, completeWorkItemDto);
        logger.info("completeWorkItem completeTask timeCost = {}",System.currentTimeMillis()-completeStart);

        //5.获取完成任务后，同一实例下运行中的待办任务
        List<Task> afterCompleteTasks = getCurrentTasks(processInstance);

        //6.过虑出新生成的任务，将数据回传给第三方系统
        List<Task> needToCallbackTasks = resolveCallbackTasks(beforeCompleteTasks, afterCompleteTasks);
        if (needToCallbackTasks.size() > 0) {
            taskExecutor.execute(new CallbackRunner(processInstance, needToCallbackTasks));
        }

        //7.如果参数中有紧急字段，则更新到flow_process_intance_trace中
        Map<String,Object> flowParam = completeWorkItemDto.getFlowPassList();
        if (MapUtils.isNotEmpty(flowParam)){
            Object urgentLevelObj = flowParam.get(ConstanceVariable.URGENT_LEVEL);
            if (Objects.nonNull(urgentLevelObj)){
                int urgentLevel = (int) urgentLevelObj;
                FlowProcessInstanceTraceVo flowProcessInstanceTraceVo = flowProcessInstanceTraceRepository.getProcessInstanceTraceByProcessInstanceId(processInstance.getProcessInstanceId());
                flowProcessInstanceTraceVo.setUrgentLevel(urgentLevel);
                List<FlowProcessInstanceTraceVo> flowProcessInstanceTraceVoList = new ArrayList<>();
                flowProcessInstanceTraceVoList.add(flowProcessInstanceTraceVo);
                flowProcessInstanceTraceRepository.update(flowProcessInstanceTraceVoList);
                // 完成任务成功时,紧急程度需要更新同步给待办组件
                syncProcessTaskProcessor.syncDataByProcess(flowProcessInstanceTraceVo.getProcessInstanceId(), urgentLevel);
            }
        }

        HttpCommandResultDto result = resolveResult(processInstance, null, COMPLETE_WORK_ITEM_SUCCESS);
        //7.标记一下流程是否已经走完
        resolveProcessInstanceCompletedFlag(result);
        // 8.保存流程实例中处理关系信息
        completeWorkItemDto.setFlowParamList(variables);
        this.saveFormProcessInstanceHandleRel(processInstance, completeWorkItemDto);


        return result;
    }

    /**
     * 处理加签父任务
     */
    private void handleAddSignParentTask(Task task,HttpCompleteWorkItemDto completeWorkItemDto){
        String parentTaskId = task.getParentTaskId();

        if (StringUtils.isNotBlank(parentTaskId)) {
            boolean completed = false;
            Task taskParent = taskService.createTaskQuery().taskId(parentTaskId).singleResult();
            if (AFTER_ADDSIGN.equals(taskParent.getScopeType())) {
                String approval = "pass";
                if(Objects.nonNull(completeWorkItemDto.getFlowPassList()) && Objects.nonNull(completeWorkItemDto.getFlowPassList().get("approval"))){
                    approval =   String.valueOf(completeWorkItemDto.getFlowPassList().get("approval"));
                }
                //如果处理结果是不通过
                //则完成父任务，剩余的子任务也标记为完成
                if (!Objects.equals(approval,"pass")){
                    List<Task> taskList = taskService.createTaskQuery().processInstanceId(taskParent.getProcessInstanceId()).taskDefinitionKey(taskParent.getTaskDefinitionKey()).list();
                    //递归处理委派后再次加签的情况
                    dealSubTask(taskParent,taskList);

                    completed = true;

                    //处理父任务
                    taskService.complete(parentTaskId, completeWorkItemDto.getFlowPassList());

                    //如果当前任务是委派后再加签，则加签的人处理意见为驳回，那么需要将委派的任务也给完成掉
                    String delegeTaskId = taskParent.getParentTaskId();
                    if (StringUtils.isNotBlank(delegeTaskId)){
                        Task delegateTask = taskService.createTaskQuery().taskId(delegeTaskId).singleResult();
                        if (Objects.nonNull(delegateTask) && DelegationState.PENDING.equals(delegateTask.getDelegationState())){
                            unPauseFlowTask(delegeTaskId);
                        }
                    }
                }
            }


            String tableName = managementService.getTableName(TaskEntity.class);
            String sql = "select count(1) from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}";
            long subTaskCount = taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count();
            if (subTaskCount == 0 && !completed) {
                //处理前后加签的任务、委派任务
                unPauseFlowTask(parentTaskId);

                if (AFTER_ADDSIGN.equals(taskParent.getScopeType())) {
                    taskService.complete(parentTaskId, completeWorkItemDto.getFlowPassList());
                }
            }
        }
    }

    private void unPauseFlowTask(String delegeTaskId) {
        FlowTaskTraceVo flowTaskTrace = flowTaskTraceRepository.getTaskTraceByTaskId(delegeTaskId);
        if (Objects.nonNull(flowTaskTrace)){
            if (ProcessInstanceStatusEnum.PAUSE.getCode().equals(flowTaskTrace.getStatus())){
                flowTaskTrace.setStatus(ProcessInstanceStatusEnum.NORMAL.getCode());
                flowTaskTraceRepository.updateTaskTraceById(flowTaskTrace);
            }
        }
        //将委派任务完成掉
        taskService.resolveTask(delegeTaskId);
    }
    private void setFlowTaskDone(String delegeTaskId) {
        FlowTaskTraceVo flowTaskTrace = flowTaskTraceRepository.getTaskTraceByTaskId(delegeTaskId);
        if (Objects.nonNull(flowTaskTrace)){
            flowTaskTrace.setLinkStatus(TaskLinkStatusEnum.DONE.getCode());
            flowTaskTraceRepository.updateTaskTraceById(flowTaskTrace);
        }
    }
    public void dealSubTask(Task parentTask,List<Task> taskList){
        if (Objects.nonNull(taskList) && taskList.size() > 0){
            for (Task todoTask : taskList) {
                //处理所有子任务
                if (Objects.equals(todoTask.getParentTaskId(),parentTask.getId())){
                    if (Objects.equals(DelegationState.PENDING,todoTask.getDelegationState())){
                        //递归处理委派的情况
                        dealSubTask(todoTask,taskList);
                        taskService.resolveTask(todoTask.getId());
                    }else{
                        taskService.complete(todoTask.getId());
                    }
                    //更新flowTask状态为完成
                    setFlowTaskDone(todoTask.getId());
                }
            }
        }
    }



    private void recordTaskInfoToThreadLocal(Task task) {
        CommonOperateLogHolder.getCommonOperateLog().setTaskName(task.getName());
        CommonOperateLogHolder.getCommonOperateLog().setActivityId(task.getTaskDefinitionKey());
    }

    private void recordRequestInfoToThreadLocal(HttpCompleteWorkItemDto completeWorkItemDto) {
        CommonOperateLogHolder.getCommonOperateLog().setTaskId(completeWorkItemDto.getWorkItemId());
        CommonOperateLogHolder.getContent().setRequest(completeWorkItemDto);
    }

    private void resolveProcessInstanceCompletedFlag(HttpCommandResultDto result) {
        Map<String, String> resultParams = new HashMap<>(2);
        resultParams.put("processInstanceCompletedFlag", CommonOperateLogHolder.getProcessInstanceCompletedFlag().toString());
        result.setFlowPassList(resultParams);

    }

    private List<Task> resolveCallbackTasks(List<Task> beforeCompleteTasks, List<Task> afterCompleteTasks) {
        List<Task> result = new ArrayList<>();
        for (Task task : afterCompleteTasks) {
            if (!existInBeforeTask(task, beforeCompleteTasks)) {
                result.add(task);
            }
        }
        return result;
    }

    private boolean existInBeforeTask(Task task, List<Task> beforeCompleteTasks) {
        for (Task t : beforeCompleteTasks) {
            if (Objects.equals(task.getId(), t.getId())) {
                return true;
            }
        }
        return false;
    }

    private List<Task> getCurrentTasks(ProcessInstance processInstance) {
        return taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).list();
    }

    private Task resolveTask(HttpCompleteWorkItemDto completeWorkItemDto) {
        if (StringUtils.isEmpty(completeWorkItemDto.getWorkItemId())) {
            logger.warn("未传递工单id,值为{}", completeWorkItemDto.getWorkItemId());
            return null;
        }
        Task task = taskService.createTaskQuery().processInstanceId(completeWorkItemDto.getProcessInstanceId()).taskId(completeWorkItemDto.getWorkItemId()).singleResult();
        if (Objects.isNull(task)) {
            logger.warn("工单id: {}对应的工单不存在", completeWorkItemDto.getWorkItemId());
            return null;
        }
        return task;
    }

    private void completeTask(Task task, HttpCompleteWorkItemDto completeWorkItemDto) {
        //若传递了人员信息，则设置为办理人
        if (StringUtils.isNotEmpty(completeWorkItemDto.getParticipantId())) {
            taskService.setAssignee(task.getId(), completeWorkItemDto.getParticipantId());
            CommonOperateLogHolder.setParticipantName(completeWorkItemDto.getParticipantName());
            publishEvent(completeWorkItemDto);
        }
        String formKey = task.getFormKey();
        Map<String, Object> variables = completeWorkItemDto.getFlowParamList();
        if (StringUtils.isNotEmpty(formKey) && MapUtil.isNotEmpty(variables)) {
            //将variable当成表单数据保存到数据库
            formInstanceRepository.insertFormInstance(resolveFromInstance(task, variables));
        }
        taskService.complete(task.getId(), variables, true);

        //9.处理加签父任务
        this.handleAddSignParentTask(task ,completeWorkItemDto);
        //taskService.complete(task.getId(), completeWorkItemDto.getFlowParamObjectMap(), true);
    }

    private FormInstanceDto resolveFromInstance(Task task, Map<String, Object> variables) {
        FormInstanceDto formInstance = new FormInstanceDto();
        formInstance.setName(task.getFormKey() + ".form");
        formInstance.setTaskId(task.getId());
        formInstance.setProcessInstanceId(task.getProcessInstanceId());
        formInstance.setResource(JSONObject.parseObject(JSONObject.toJSONString(variables)));
        return formInstance;
    }

    private void publishEvent(HttpCompleteWorkItemDto completeWorkItemDto) {
        CreateParticipantEvent.CreateParticipantVo createParticipantVo = new CreateParticipantEvent.CreateParticipantVo();
        BeanUtils.copyProperties(completeWorkItemDto, createParticipantVo);
        SpringUtils.publishEvent(new CreateParticipantEvent(createParticipantVo));
    }

    /**
     * 保存流程实例中处理关系信息
     *
     * @param processInstance
     * @param completeWorkItemDto
     */
    private void saveFormProcessInstanceHandleRel(ProcessInstance processInstance, HttpCompleteWorkItemDto completeWorkItemDto) {
        // 保存流程实例与处理人(这里是阅办人)的关系信息
        try {
            Map<String, String> map = new HashMap<>();
            map.put("readDoJumpUrl", readDoJumpUrl);
            map.put("sourceAppCode", sourceAppCode);
            map.put("participantId", completeWorkItemDto.getParticipantId());
            map.put("participantName", completeWorkItemDto.getParticipantName());
            List<FormProcessInstanceHandleRelDto> formProcessInstanceHandleRelDtos = FormProcessInstanceHandleRelConvertUtil.convertToProcessInstanceHandleRelDto(
                    processInstance.getProcessInstanceId(), completeWorkItemDto.getFlowParamList(), map,
                    completeWorkItemDto.getWorkItemId(), ReadOperateTypeEnum.COMPLETEWORKITEM.getCode());
            handleRelRepository.batchInsertFlowFormProcessInstanceHandleRel(formProcessInstanceHandleRelDtos);
        } catch (Exception ex) {
            logger.error("发起流程存储阅办人关联信息时失败,流程实例ID:" + processInstance.getProcessInstanceId() + ",失败信息：" + ex.getMessage(), ex);
        }
    }

    private class CallbackRunner implements Runnable {

        private ProcessInstance processInstance;
        private List<Task> tasks;

        CallbackRunner(ProcessInstance processInstance, List<Task> tasks) {
            this.processInstance = processInstance;
            this.tasks = tasks;
        }

        @Override
        public void run() {
            callbackThirdPlatformAfterTaskCreatedService.handle(tasks);
        }
    }
}
